package com.lancoo.cvas.arrangecourse.rulecheck;

import com.lancoo.cvas.arrangecourse.common.BinaryUtil;
import com.lancoo.cvas.arrangecourse.common.StrUtil;
import com.lancoo.cvas.arrangecourse.entities.BaseRule;
import com.lancoo.cvas.arrangecourse.entities.ClassHour;
import com.lancoo.cvas.arrangecourse.entities.TeachingClassUnit;
import com.lancoo.cvas.arrangecourse.entities.TimeslotRoom;

import java.util.LinkedList;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @Description 无冲突排课的规则检测，最基础的，检测禁排规则和排课教师和学生冲突问题
 * @Author Fortysun
 * @Date 2023/9/5 17:12
 **/
public class ClashRuleCheck extends RuleCheck implements RuleDecorator{
    //接收父类的规则服务对象
    private RuleService ruleService;
    public ClashRuleCheck(RuleService ruleService, BaseRule baseRule) {
        this.ruleService = ruleService;
        super.baseRule = baseRule;
    }
    /**
     * 检测是否适合分配：
     * 1、判断是否满足课程分散、集中规则
     * 2、判断是否满足教师分散、集中规则
     * 3、判断是否满足教师单日最大节次安排
     * @param teachingClassUnit 教学班排课单元
     * @param continuousList 连上的教室课时分组信息列表
     * @param classHourMap 课表课时map
     * @return
     */
    @Override
    public Boolean isSuitable(TeachingClassUnit teachingClassUnit,
                              LinkedList<TimeslotRoom> continuousList,
                              ConcurrentHashMap<String, CopyOnWriteArrayList<ClassHour>> classHourMap) {
        //如果上一个增强的规则是符合要求的
        if (ruleService.isSuitable(teachingClassUnit, continuousList, classHourMap)) {
            //进行冲突排课的规则的检测
            return isClashRuleSuitable(teachingClassUnit, continuousList);
        }

        return false;
    }

    /**
     * 冲突排课的，最基本的规则，是否符合要求
     * @param teachingClassUnit 教学班排课单元
     * @param continuousList 连上的教室课时分组信息列表
     * @return
     */
    private Boolean isClashRuleSuitable(TeachingClassUnit teachingClassUnit,
                                          LinkedList<TimeslotRoom> continuousList) {
        //如果连上的教室课时分组列表信息为空，或者没有内容
        if (continuousList == null || continuousList.isEmpty()) {
//            log.error("#ClashRuleCheck.isClashRuleSuitable#:分配的教室课时分组信息列表为空~！！");
            return false;
        }

        //教学班所需的周次信息
        String teachingClassWeeks = teachingClassUnit.getClassWeeks();
        //教学班禁排的课时编码字符串，多个以逗号分隔
        String prohibitTimeslots = teachingClassUnit.getProhibitTimeslotCodes();
        for (TimeslotRoom timeslotRoom : continuousList) {
            String timeslotCode = timeslotRoom.getTimeslotCode();
            //教室课时分组中的周次字符串
            String timeslotRoomWeeks = timeslotRoom.getWeeks();
            if ((prohibitTimeslots != null && prohibitTimeslots.contains(timeslotCode))//如果属于禁排的课时
                    //如果可分配的周数信息，小于所需总周数的一半
                    || !isWeeksSuitable(teachingClassWeeks, timeslotRoomWeeks)
            ) {//如果当前安排，与其他教学班存在排课冲突
                //当前课时分组列表不合适，不能安排
                return false;
            }
        }

        //可以安排
        return true;
    }

    /**
     * 检测周次，是否符合冲突排课规则
     * 标准：待分配的教学班的周次，至少要一半能够得到满足，才分配
     * @param teachingClassWeeks 待分配的教学班的周次信息
     * @param timeslotRoomWeeks 待分配给教学班的教室课时分组信息,可以用来分配的周次信息
     * @return
     */
    private boolean isWeeksSuitable(String teachingClassWeeks, String timeslotRoomWeeks) {
        //将两个二进制字符串，进行与操作,比如10101和11111，进行与操作，结果是10101
        String result = BinaryUtil.binaryAnd(timeslotRoomWeeks, teachingClassWeeks);
        //分配给教学班的周次中，教学班可用的周数
        int oneNumOfResult = StrUtil.numOfOne(result);
        //教学班的周次信息中，总的可用的周数
        int oneNumOfTeachingClassWeeks = StrUtil.numOfOne(teachingClassWeeks);
        //如果可分配的周数信息，大于所需总周数的一半
        return oneNumOfTeachingClassWeeks < oneNumOfResult*2;
    }
}
